<?php
namespace app\services;

use app\models\LampUser;
use Yii;
use yii\helpers\Json;
use app\models\User;
use yii\redis\Connection;

class LampUserService

{
    private const LIST_CACHE_PREFIX = 'lamp:user:list:';
    private const LIST_CACHE_TTL = 1800; // 30分钟
    private const MAX_CACHE_PAGE = 5;    // 只缓存前5页

    /**
     * 后台用户分页搜索（带缓存）
     * @param array $params 筛选条件 ['status' => 1, 'role' => 'admin']
     * @param int $page 页码
     * @param int $pageSize 每页数量
     * @return array ['items' => [...], 'totalCount' => 100]
     */
    public function search(array $params, int $page = 1, int $pageSize = 20): array
    {
        // 1. 生成条件哈希（防Key爆炸）
        $hash = $this->generateConditionHash($params);
        $cacheKey = self::LIST_CACHE_PREFIX . $hash . ':page:' . $page;

        // 2. 尝试从缓存获取ID列表
        $uidList = $this->getIdListFromCache($cacheKey, $page, $pageSize, $params);

        // 3. 批量获取用户详情（复用 user:info:{uid} 缓存）
        $users = $this->batchGetUserDetails($uidList);

        // 4. 获取总数量（单独缓存，避免分页查询count）
        $totalCount = $this->getTotalCount($params, $hash);

        return [
            'items' => $users,
            'totalCount' => $totalCount,
        ];
    }

    /**
     * 生成筛选条件哈希（排序+MD5）
     */
    private function generateConditionHash(array $params): string
    {
        ksort($params); // 确保相同条件顺序一致
        $str = http_build_query($params);
        return md5($str);
    }

    /**
     * 从缓存获取ID列表（带防击穿）
     */
    private function getIdListFromCache(string $cacheKey, int $page, int $pageSize, array $params): array
    {
        $redis = Yii::$app->redis;

        // 尝试获取缓存
        $cached = $redis->get($cacheKey);
        if ($cached !== false) {
            return Json::decode($cached) ?: [];
        }

        // 🔒 防缓存击穿：使用Redis SETNX实现分布式锁
        $lockKey = $cacheKey . ':lock';
        $lockAcquired = $redis->set($lockKey, 1, 'EX', 5, 'NX');

        try {
            if ($lockAcquired) {
                // 双重检查（避免锁竞争后重复查询）
                $cached = $redis->get($cacheKey);
                if ($cached !== false) {
                    return Json::decode($cached) ?: [];
                }

                // 查询数据库ID列表
                $uidList = $this->queryIdListFromDb($params, $page, $pageSize);

                // 写入缓存（空结果也缓存防穿透）
                $ttl = empty($uidList) ? 60 : self::LIST_CACHE_TTL;
                $redis->setex($cacheKey, $ttl, Json::encode($uidList));

                return $uidList;
            } else {
                // 未获取到锁，等待后重试（或降级查DB）
                usleep(100000); // 100ms
                return $this->getIdListFromCache($cacheKey, $page, $pageSize, $params);
            }
        } finally {
            if ($lockAcquired) {
                $redis->del($lockKey);
            }
        }
    }

    /**
     * 从DB查询ID列表（只查主键）
     */
    private function queryIdListFromDb(array $params, int $page, int $pageSize): array
    {
        $query = self::queryFind($params);
        $query->select('id');

        // 排序（注意：分页必须有确定排序）
        $query->orderBy(['id' => SORT_DESC]);

        // 分页
        $offset = ($page - 1) * $pageSize;
        return $query->limit($pageSize)->offset($offset)->column();
    }

    public function queryFind($params)
    {
        $query = LampUser::find();
        // 应用筛选条件（示例）
        //if (isset($params['status'])) {
            $query->andFilterWhere(['status' => $params['status'] ?? null]);
        //}
        //if (!empty($params['keyword'])) {
            $query->andFilterWhere(['like', 'nickname', $params['keyword'] ?? '']);
        //}
        return $query;
    }

    /**
     * 批量获取用户详情（复用 user:info:{uid} 缓存）
     */
    private function batchGetUserDetails(array $uidList): array
    {
        if (empty($uidList)) {
            return [];
        }

        $redis = Yii::$app->redis;
        foreach ($uidList as $uid) {
            $cachedResults[$uid] = $redis->hgetall("user:info:{$uid}");
        }

        // 组装结果 & 补漏
        $users = [];
        $missingUids = [];

        foreach ($uidList as $index => $uid) {
            $data = $cachedResults[$index] ?? [];
            if (empty($data)) {
                $missingUids[] = $uid;
            } else {
                $users[] = $this->buildUserFromHash($data);
            }
        }

        // 补漏：查DB并异步更新缓存
        if (!empty($missingUids)) {
            $dbUsers = LampUser::find()->where(['id' => $missingUids])->asArray()->all();

            // 异步更新缓存（避免阻塞）
            $this->asyncUpdateUserCache($dbUsers);

            foreach ($dbUsers as $user) {
                $users[] = $this->buildUserFromHash($user);
            }
        }

        return $users;
    }

    /**
     * 从Hash构建用户对象
     */
    private function buildUserFromHash(array $data): array
    {
        // 假设Hash结构: ['id'=>'10086', 'nickname'=>'张三', ...]
        return [
            'id' => (int)($data['id'] ?? 0),
            'nickname' => $data['nickname'] ?? '',
            'email' => $data['email'] ?? '',
            'status' => (int)($data['status'] ?? 1),
            'ctime' => $data['ctime'] ?? '',
        ];
    }

    /**
     * 异步更新用户缓存（用队列或defer执行）
     */
    private function asyncUpdateUserCache(array $users): void
    {
        //生产环境建议用队列（如RabbitMQ）
        // Yii::$app->queue->push(new UpdateUserCacheJob(['users' => $users]));
        $redis = Yii::$app->redis;
        $redis = Yii::$app->redis;

        foreach ($users as $user) {
            $key = "user:info:{$user['id']}";
            $redis->hmset($key,
                'id', $user['id'],
                'nickname', $user['nickname'],
                'email', $user['email'],
                'status', $user['status'],
                'ctime', $user['ctime']
            );
            $redis->expire($key, 7200); // 2小时
        }
    }

    /**
     * 获取总数量（单独缓存）
     */
    private function getTotalCount(array $params, string $hash): int
    {
        $totalCountKey = self::LIST_CACHE_PREFIX . $hash . ':total';
        $redis = Yii::$app->redis;

        $count = $redis->get($totalCountKey);
        if ($count === false) {
            $count = self::queryFind($params)->count();

            $redis->setex($totalCountKey, 300, $count); // 5分钟
        }

        return (int)$count;
    }
}